use serde::{Deserialize, Serialize};

////////////////////////////////
/// rust api call result
////////////////////////////////
#[derive(thiserror::Error, Debug, Serialize, Deserialize)]
pub struct ReqResult {
    #[serde(rename = "code")]
    pub code: i64,

    #[serde(rename = "data")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<serde_json::Value>,

    #[serde(rename = "msg")]
    pub msg: Option<String>,
}

pub type Result<T> = std::result::Result<T, anyhow::Error>;
use std::fmt::Display;
use thiserror::Error;
/// Sdk custome error
/// anyhow::Error may be downcast to this
#[derive(Error, Deserialize, Serialize, Debug, Clone, PartialEq)]
pub enum ReqResultError {
    #[error("Srv not found any long-term srv node")]
    ErrorNoLongTermSrvNodeFound, // 40001
    #[error("Client appkey error")]
    ErrorAppKeyEmpty, // 40002
    #[error("Client appkey error")]
    ErrorAppKeyWrong, // 40003
    #[error("Client sign error")]
    ErrorSignEmpty, // 40004
    #[error("Client sign error")]
    ErrorSignWrong, // 40005
    #[error("Client sign error")]
    ErrorSignRuntime, // 40006
    #[error("Srv not found any turn distribute srv node")]
    ErrorNoTurnSrvNodeFound, // 40007
    #[error("Internal server error")]
    ErrorInternalSrvError, // 40008
    #[error("Invalid query params")]
    ErrorInvalidQueryParams, // 40009
    #[error("Invalid body params")]
    ErrorInvalidBodyParams, // 40010
    #[error("Not Acceptable")]
    ErrorNotAcceptable, // 40601
    #[error("Permission not allowed")]
    ErrorPermissionNotAllow, // 40011
}

// from anyhow::Error
impl From<anyhow::Error> for ReqResult {
    fn from(err: anyhow::Error) -> Self {
        match err.downcast::<ReqResult>() {
            Ok(err) => err,
            Err(err) => {
                let cc_error = err.downcast_ref::<ReqResultError>();
                if let Some(err) = cc_error {
                    match err {
                        ReqResultError::ErrorNoLongTermSrvNodeFound => {
                            ReqResult::error_code_string(
                                40001,
                                ReqResultError::ErrorNoLongTermSrvNodeFound.to_string(),
                            )
                        }
                        ReqResultError::ErrorAppKeyEmpty => ReqResult::error_code_string(
                            40002,
                            ReqResultError::ErrorAppKeyEmpty.to_string(),
                        ),
                        ReqResultError::ErrorAppKeyWrong => ReqResult::error_code_string(
                            40003,
                            ReqResultError::ErrorAppKeyWrong.to_string(),
                        ),
                        ReqResultError::ErrorSignEmpty => ReqResult::error_code_string(
                            40004,
                            ReqResultError::ErrorSignEmpty.to_string(),
                        ),
                        ReqResultError::ErrorSignWrong => ReqResult::error_code_string(
                            40005,
                            ReqResultError::ErrorSignWrong.to_string(),
                        ),
                        ReqResultError::ErrorSignRuntime => ReqResult::error_code_string(
                            40006,
                            ReqResultError::ErrorSignRuntime.to_string(),
                        ),
                        ReqResultError::ErrorNoTurnSrvNodeFound => ReqResult::error_code_string(
                            40007,
                            ReqResultError::ErrorNoTurnSrvNodeFound.to_string(),
                        ),
                        ReqResultError::ErrorInternalSrvError => ReqResult::error_code_string(
                            40008,
                            ReqResultError::ErrorInternalSrvError.to_string(),
                        ),
                        ReqResultError::ErrorInvalidQueryParams => ReqResult::error_code_string(
                            40009,
                            ReqResultError::ErrorInvalidQueryParams.to_string(),
                        ),
                        ReqResultError::ErrorInvalidBodyParams => ReqResult::error_code_string(
                            40010,
                            ReqResultError::ErrorInvalidBodyParams.to_string(),
                        ),
                        ReqResultError::ErrorNotAcceptable => ReqResult::error_code_string(
                            40601,
                            ReqResultError::ErrorNotAcceptable.to_string(),
                        ),
                        ReqResultError::ErrorPermissionNotAllow => ReqResult::error_code_string(
                            40011,
                            ReqResultError::ErrorPermissionNotAllow.to_string(),
                        ),
                    }
                } else {
                    ReqResult::error_string(err.to_string())
                }
            }
        }
    }
}

impl From<serde_json::Error> for ReqResult {
    fn from(err: serde_json::Error) -> Self {
        ReqResult::error_string(err.to_string())
    }
}

impl From<Result<()>> for ReqResult {
    fn from(res: Result<()>) -> Self {
        match res {
            Ok(()) => ReqResult::ok(),
            Err(err) => err.into(),
        }
    }
}
impl From<Result<String>> for ReqResult {
    fn from(res: Result<String>) -> Self {
        match res {
            Ok(msg) => {
                let result = serde_json::from_str(&msg);
                match result {
                    Err(e) => e.into(),
                    Ok(msg) => ReqResult::ok_data(msg),
                }
            }
            Err(err) => err.into(),
        }
    }
}

impl ReqResult {
    pub fn error_with_code(code: i64) -> Self {
        ReqResult {
            code: if code == 0 { -1 } else { code },
            msg: None,
            data: None,
        }
    }
    pub fn error_code_str(code: i64, msg_str: &str) -> Self {
        ReqResult {
            code: if code == 0 { -1 } else { code },
            msg: Some(msg_str.to_owned()),
            data: None,
        }
    }
    pub fn error_str(msg_str: &str) -> Self {
        ReqResult {
            code: -1,
            msg: Some(msg_str.to_owned()),
            data: None,
        }
    }
    pub fn error_string(msg: String) -> Self {
        ReqResult {
            code: -1,
            msg: Some(msg),
            data: None,
        }
    }
    pub fn error_code_string(code: i64, msg: String) -> Self {
        ReqResult {
            code: if code == 0 { -1 } else { code },
            msg: Some(msg),
            data: None,
        }
    }
    pub fn ok() -> Self {
        ReqResult {
            code: 0,
            msg: None,
            data: None,
        }
    }
    pub fn ok_msg(msg: String) -> Self {
        ReqResult {
            code: 0,
            msg: Some(msg),
            data: None,
        }
    }
    pub fn ok_data(data: serde_json::Value) -> Self {
        ReqResult {
            code: 0,
            msg: None,
            data: Some(data),
        }
    }
    pub fn ok_data_and_msg(data: serde_json::Value, msg: String) -> Self {
        ReqResult {
            code: 0,
            msg: Some(msg),
            data: Some(data),
        }
    }
}

impl Display for ReqResult {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!(
            "ReqResult code: {}, msg: {}",
            self.code,
            self.msg.clone().unwrap_or("".to_string())
        ))
    }
}
